<template>
  <div class="line-basic">
    <h1>线</h1>
    <p>在WebGL中，线是由两个或多个点连接而成的基本图形。通过线的绘制，我们可以学习如何在WebGL中处理多个顶点数据和不同的绘制模式。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>线的绘制</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="demoCanvas" width="400" height="300"></canvas>
        <div class="control-panel">
          <el-form :model="lineForm" label-width="100px">
            <el-form-item label="线的宽度">
              <el-slider v-model="lineForm.width" :min="1" :max="maxLineWidth" @input="updateLine" />
              <div class="line-width-tip" v-if="maxLineWidth === 1">
                注意：当前环境仅支持1.0的线宽
              </div>
            </el-form-item>
            <el-form-item label="线的颜色">
              <el-color-picker v-model="lineForm.color" @change="updateLine" />
            </el-form-item>
            <el-form-item label="绘制模式">
              <el-select v-model="lineForm.mode" @change="updateLine">
                <el-option label="LINES" value="LINES" />
                <el-option label="LINE_STRIP" value="LINE_STRIP" />
                <el-option label="LINE_LOOP" value="LINE_LOOP" />
              </el-select>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
attribute vec4 a_Position;
void main() {
  gl_Position = a_Position;
}

// 片元着色器
precision mediump float;
uniform vec4 u_FragColor;
void main() {
  gl_FragColor = u_FragColor;
}

// JavaScript代码
const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');

// 创建着色器程序...

// 设置顶点位置
const vertices = new Float32Array([
  -0.5, -0.5,
   0.5, -0.5,
   0.5,  0.5,
  -0.5,  0.5
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 绘制线
gl.drawArrays(gl.LINE_LOOP, 0, 4);
        </code></pre>
      </div>
    </el-card>

    <el-card class="info-section">
      <template #header>
        <div class="card-header">
          <h3>绘制模式说明</h3>
        </div>
      </template>
      <el-descriptions :column="1" border>
        <el-descriptions-item label="LINES">
          将顶点两两配对绘制独立的线段
        </el-descriptions-item>
        <el-descriptions-item label="LINE_STRIP">
          将顶点依次连接，形成一条连续的折线
        </el-descriptions-item>
        <el-descriptions-item label="LINE_LOOP">
          在LINE_STRIP的基础上，将最后一个顶点与第一个顶点相连，形成闭合的折线
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'

const demoCanvas = ref<HTMLCanvasElement | null>(null)
const gl = ref<WebGLRenderingContext | null>(null)
const shaderProgram = ref<WebGLProgram | null>(null)
const maxLineWidth = ref(1);

const lineForm = reactive({
  width: 1,
  color: '#409EFF',
  mode: 'LINE_LOOP'
})

// 将十六进制颜色转换为RGB
const hexToRgb = (hex: string) => {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  return result ? {
    r: parseInt(result[1], 16) / 255,
    g: parseInt(result[2], 16) / 255,
    b: parseInt(result[3], 16) / 255
  } : { r: 0, g: 0, b: 0 }
}

// 创建着色器
const createShader = (type: number, source: string) => {
  const shader = gl.value!.createShader(type)
  if (!shader) {
    console.error('Failed to create shader')
    return null
  }

  gl.value!.shaderSource(shader, source)
  gl.value!.compileShader(shader)

  if (!gl.value!.getShaderParameter(shader, gl.value!.COMPILE_STATUS)) {
    console.error('Failed to compile shader:', gl.value!.getShaderInfoLog(shader))
    gl.value!.deleteShader(shader)
    return null
  }

  return shader
}

// 初始化WebGL
const initWebGL = () => {
  if (!demoCanvas.value) return

  // 获取WebGL上下文
  gl.value = demoCanvas.value.getContext('webgl')
  if (!gl.value) {
    console.error('Failed to get WebGL context')
    return
  }

  // 检查最大支持的线宽
  maxLineWidth.value = Math.floor(gl.value.getParameter(gl.value.ALIASED_LINE_WIDTH_RANGE)[1]);
  console.log('Maximum supported line width:', maxLineWidth.value);

  // 顶点着色器源码
  const vertexShaderSource = `
    attribute vec4 a_Position;
    void main() {
      gl_Position = a_Position;
    }
  `

  // 片元着色器源码
  const fragmentShaderSource = `
    precision mediump float;
    uniform vec4 u_FragColor;
    void main() {
      gl_FragColor = u_FragColor;
    }
  `

  // 创建着色器
  const vertexShader = createShader(gl.value.VERTEX_SHADER, vertexShaderSource)
  const fragmentShader = createShader(gl.value.FRAGMENT_SHADER, fragmentShaderSource)
  if (!vertexShader || !fragmentShader) return

  // 创建程序并链接着色器
  shaderProgram.value = gl.value.createProgram()
  if (!shaderProgram.value) {
    console.error('Failed to create shader program')
    return
  }

  gl.value.attachShader(shaderProgram.value, vertexShader)
  gl.value.attachShader(shaderProgram.value, fragmentShader)
  gl.value.linkProgram(shaderProgram.value)

  if (!gl.value.getProgramParameter(shaderProgram.value, gl.value.LINK_STATUS)) {
    console.error('Failed to link program:', gl.value.getProgramInfoLog(shaderProgram.value))
    return
  }

  gl.value.useProgram(shaderProgram.value)

  // 设置顶点数据
  const vertices = new Float32Array([
    -0.5, -0.5,
     0.5, -0.5,
     0.5,  0.5,
    -0.5,  0.5
  ])
  const vertexBuffer = gl.value.createBuffer()
  gl.value.bindBuffer(gl.value.ARRAY_BUFFER, vertexBuffer)
  gl.value.bufferData(gl.value.ARRAY_BUFFER, vertices, gl.value.STATIC_DRAW)

  // 获取attribute变量位置
  const a_Position = gl.value.getAttribLocation(shaderProgram.value, 'a_Position')
  gl.value.vertexAttribPointer(a_Position, 2, gl.value.FLOAT, false, 0, 0)
  gl.value.enableVertexAttribArray(a_Position)

  // 设置清除颜色并清除
  gl.value.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.value.clear(gl.value.COLOR_BUFFER_BIT)

  // 绘制线条
  updateLine()
}

// 更新线条属性并重绘
const updateLine = () => {
  if (!gl.value || !shaderProgram.value) return

  // 设置线宽
  gl.value.lineWidth(lineForm.width)

  // 更新颜色
  const color = hexToRgb(lineForm.color)
  const u_FragColor = gl.value.getUniformLocation(shaderProgram.value, 'u_FragColor')
  gl.value.uniform4f(u_FragColor, color.r, color.g, color.b, 1.0)

  // 清除并重绘
  gl.value.clear(gl.value.COLOR_BUFFER_BIT)
  let drawMode;
  switch (lineForm.mode) {
    case 'LINES':
      drawMode = gl.value.LINES;
      break;
    case 'LINE_STRIP':
      drawMode = gl.value.LINE_STRIP;
      break;
    case 'LINE_LOOP':
      drawMode = gl.value.LINE_LOOP;
      break;
    default:
      drawMode = gl.value.LINE_LOOP;
  }
  gl.value.drawArrays(drawMode, 0, 4)
}

// 组件挂载时初始化WebGL
onMounted(() => {
  initWebGL()
})
</script>

<style lang="scss" scoped>
.line-basic {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    color: #303133;
    margin-bottom: 20px;
  }

  .demo-section,
  .code-section,
  .info-section {
    margin: 30px 0;

    .card-header {
      display: flex;
      align-items: center;

      h3 {
        margin: 0;
        color: #409EFF;
      }
    }
  }

  .demo-section {
    .demo-content {
      display: flex;
      gap: 20px;

      canvas {
        border: 1px solid #dcdfe6;
        background-color: #000;
      }

      .control-panel {
        flex: 1;
        padding: 20px;
        background-color: #f8f9fa;
        border-radius: 4px;
      }
    }
  }

  .code-section,
  .info-section {
    .code-content {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;

      code {
        font-family: monospace;
        color: #409EFF;
      }

      pre {
        margin: 0;
        white-space: pre-wrap;
      }
    }
  }

  .line-width-tip {
    font-size: 12px;
    color: #e6a23c;
    margin-top: 5px;
  }
}
</style>
